/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hit.reports;

import hit.core.Barcode;
import hit.core.HomeInventoryTracker;
import hit.core.Item;
import hit.core.ItemFacade;
import hit.core.ItemIndex;
import hit.core.Product;
import hit.core.ProductContainer;
import hit.core.ProductGroup;
import hit.core.ProductIndex;
import hit.core.Size;
import hit.core.StorageUnit;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TimeZone;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class ProductStatisticsReportTest {

	//ProductStatisticsReport report;
	TestBuilder builder;
	HomeInventoryTracker hit;
	ItemFacade itemFacade;
	StorageUnit container;
	
    public ProductStatisticsReportTest() {
    }

    @BeforeClass
    public static void setUpClass() {
    }

    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    public void setUp() {
		//report = new ProductStatisticsReport(# of months);
		builder = new TestBuilder();
		hit = HomeInventoryTracker.GetEmptyInstance();
		itemFacade = hit.GetItemFacade();
		container = new StorageUnit("Container");
    }

    @After
    public void tearDown() {
    }
	
	//*****************************************************
	/* Template on accessing the table row data:
	 * 
	 * List<BuilderObject> objects = builder.GetOutput();
	 * BuilderTable table = (BuilderTable)objects.get(0);
	 * TableRow aRow = (TableRow)table.GetRow(0-based row #);
	 * String result = aRow.GetItemValue(table.GetColumn(0-based column #));
	 * 
	 * 
	 * List of Columns:
	 * 0 - Description
	 * 1 - Barcode
	 * 2 - Size
	 * 3 - 3-Month Supply
	 * 4 - Supply: Cur/Avg
	 * 5 - Supply: Min/Max
	 * 6 - Supply: Used/Added
	 * 7 - Shelf Life
	 * 8 - Used Age: Avg/Max
	 * 9 - Cur Age: Avg/Max
	 * 
	 * 
	 * Copy/Paste code for a test case template:
	@Test
	public void testName(){
		//Product and Item setup
		final int MONTH_INPUT = 2;
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1 * MONTH_INPUT);
		Calendar date = (Calendar)startDate.clone();
	 
		//Report Creation
		ProductStatisticsReport report = new ProductStatisticsReport(MONTH_INPUT);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
	 
		//Analyze Report
		List<BuilderObject> objects = builder.GetOutput();
		BuilderTable table = (BuilderTable)objects.get(0);
	}
	 ******************************************************/

	@Test
	public void testMinMaxSupply() {
		//Product and Item setup
		final int MONTH_INPUT = 2;
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1 * MONTH_INPUT);
		Calendar date = (Calendar)startDate.clone();
		
		date.add(Calendar.DAY_OF_MONTH, -1); //day before report period
		Product a = InitializeNewProduct("A", date.getTime());
		
		date.add(Calendar.DAY_OF_MONTH, 11); //during report period
		Product b = InitializeNewProduct("B", date.getTime());
		itemFacade.AddItems(b, 1, date.getTime(), container);
		
		Product c = InitializeNewProduct("C", date.getTime());
		itemFacade.AddItems(c, 1, date.getTime(), container);
		date.add(Calendar.DAY_OF_MONTH, 13);
		itemFacade.AddItems(c, 2, date.getTime(), container);
		
		
		//Report creation
		ProductStatisticsReport report = new ProductStatisticsReport(MONTH_INPUT);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		//Analyze Report
		List<BuilderObject> objects = builder.GetOutput();
		BuilderTable table = (BuilderTable)objects.get(0);
		
		TableRow aRow = (TableRow)table.GetRow(0);
		String max = aRow.GetItemValue(table.GetColumn(5));
		String min = aRow.GetItemValue(table.GetColumn(5));
		max = max.substring(max.indexOf(" / ") + 3);
		min = min.substring(0, min.indexOf(" / "));
		assertEquals("0", max);
		assertEquals("0", min);
		
		TableRow bRow = (TableRow)table.GetRow(1);
		max = bRow.GetItemValue(table.GetColumn(5));
		min = bRow.GetItemValue(table.GetColumn(5));
		max = max.substring(max.indexOf(" / ") + 3);
		min = min.substring(0, min.indexOf(" / "));
		assertEquals("1", max);
		assertEquals("1", min);
		
		TableRow cRow = (TableRow)table.GetRow(2);
		max = cRow.GetItemValue(table.GetColumn(5));
		min = cRow.GetItemValue(table.GetColumn(5));
		max = max.substring(max.indexOf(" / ") + 3);
		min = min.substring(0, min.indexOf(" / "));
		assertEquals("3", max);
		assertEquals("1", min);
	}

	@Test
	public void testUsedAddedSupply(){
		//Product and Item setup
		final int MONTH_INPUT = 2;
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1 * MONTH_INPUT);
		Calendar date = (Calendar)startDate.clone();
		
		date.add(Calendar.DAY_OF_MONTH, -1); //day before report period
		Product a = InitializeNewProduct("A", date.getTime());
		
		date.add(Calendar.DAY_OF_MONTH, -1); //day before report period
		Product a2 = InitializeNewProduct("A2", date.getTime());
		List<Item> itemsAdded = itemFacade.AddItems(a2, 2, date.getTime(), container);
		date.add(Calendar.DAY_OF_MONTH, 1);
		itemFacade.RemoveItems(itemsAdded);
		
		date.add(Calendar.DAY_OF_MONTH, 11); //during report period
		Product b = InitializeNewProduct("B", date.getTime());
		itemFacade.AddItems(b, 1, date.getTime(), container);
		
		Product c = InitializeNewProduct("C", date.getTime());
		itemsAdded = itemFacade.AddItems(c, 2, date.getTime(), container);
		date.add(Calendar.DAY_OF_MONTH, 13);
		itemFacade.RemoveItems(itemsAdded);
		
		Product d = InitializeNewProduct("D", date.getTime());
		List<Item> itemAdded = itemFacade.AddItems(d, 1, date.getTime(), container);
		date.add(Calendar.DAY_OF_MONTH, 1);
		itemFacade.RemoveItems(itemAdded);
	 
		//Report Creation
		ProductStatisticsReport report = new ProductStatisticsReport(MONTH_INPUT);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
	 
		//Analyze Report
		List<BuilderObject> objects = builder.GetOutput();
		BuilderTable table = (BuilderTable)objects.get(0);
		
		TableRow aRow = (TableRow)table.GetRow(0);
		String used = aRow.GetItemValue(table.GetColumn(6));
		String added = aRow.GetItemValue(table.GetColumn(6));
		used = used.substring(0, used.indexOf(" / "));
		added = added.substring(added.indexOf(" / ") + 3);
		assertEquals("0", used);
		assertEquals("0", added);
		
		TableRow a2Row = (TableRow)table.GetRow(1);
		used = a2Row.GetItemValue(table.GetColumn(6));
		added = a2Row.GetItemValue(table.GetColumn(6));
		used = used.substring(0, used.indexOf(" / "));
		added = added.substring(added.indexOf(" / ") + 3);
		assertEquals("2", used);
		assertEquals("0", added);
		
		TableRow bRow = (TableRow)table.GetRow(2);
		used = bRow.GetItemValue(table.GetColumn(6));
		added = bRow.GetItemValue(table.GetColumn(6));
		used = used.substring(0, used.indexOf(" / "));
		added = added.substring(added.indexOf(" / ") + 3);
		assertEquals("0", used);
		assertEquals("1", added);
		
		TableRow cRow = (TableRow)table.GetRow(3);
		used = cRow.GetItemValue(table.GetColumn(6));
		added = cRow.GetItemValue(table.GetColumn(6));
		used = used.substring(0, used.indexOf(" / "));
		added = added.substring(added.indexOf(" / ") + 3);
		assertEquals("2", used);
		assertEquals("2", added);
		
		TableRow dRow = (TableRow)table.GetRow(4);
		used = dRow.GetItemValue(table.GetColumn(6));
		added = dRow.GetItemValue(table.GetColumn(6));
		used = used.substring(0, used.indexOf(" / "));
		added = added.substring(added.indexOf(" / ") + 3);
		assertEquals("1", used);
		assertEquals("1", added);
	}
	
	@Test
	public void testProductInfo(){
		//Product and Item setup
		final int MONTH_INPUT = 2;
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1 * MONTH_INPUT);
		Calendar date = (Calendar)startDate.clone();
		
		date.add(Calendar.DAY_OF_MONTH, 2);
		Product a = InitializeNewProduct("A", date.getTime());
		
		//Specify product information not customizable through InitializeNewProduct()
		Product p = new Product();
		p.SetDescription("Product");
		p.SetCreation(date.getTime());
		p.SetBarcode(new Barcode("product"));
		p.AddProductContainer(container);
		p.SetSize(new Size(1, Size.Unit.COUNT));
		p.SetShelfLife(3);
		p.SetThreeMonth(2);
		hit.GetProductFacade().AddProduct(p);
		
		//Report Creation
		ProductStatisticsReport report = new ProductStatisticsReport(MONTH_INPUT);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
	 
		//Analyze Report
		List<BuilderObject> objects = builder.GetOutput();
		BuilderTable table = (BuilderTable)objects.get(0);
		
		TableRow aRow = (TableRow)table.GetRow(0);
		String three = aRow.GetItemValue(table.GetColumn(3));
		String shelf = aRow.GetItemValue(table.GetColumn(7));
		assertEquals("", three);
		assertEquals("", shelf);
		
		TableRow pRow = (TableRow)table.GetRow(1);
		three = pRow.GetItemValue(table.GetColumn(3));
		shelf = pRow.GetItemValue(table.GetColumn(7));
		assertEquals("2", three);
		assertEquals("3 months", shelf);
	}
	
	@Test 
	public void testCurrentSupply() {
		//setup time stuff
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1);
		Calendar date = (Calendar) startDate.clone();
		date.add(Calendar.DAY_OF_MONTH, 2);
		
		StorageUnit unit = new StorageUnit("Unit");
		hit.AddStorageUnit(unit);
		Product product = InitializeNewProduct("product", date.getTime());
		Item item1 = product.CreateItem(unit, date.getTime());
		
		//test current supply is 0
		hit.GetItemIndex().AddItem(item1);
		hit.GetItemIndex().RemoveItem(item1);
		
		ProductStatisticsReport report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		List<BuilderObject> builderObjects = builder.GetOutput();
		BuilderTable table = (BuilderTable) builderObjects.get(0);
		
		TableRow row = (TableRow) table.GetRow(0);
		String currentSupply = row.GetItemValue(table.GetColumn(4));
		currentSupply = currentSupply.substring(0, currentSupply.indexOf("/")).trim();
		assertEquals("0", currentSupply);
		
		//test supply 1
		hit.GetItemIndex().AddItem(product.CreateItem(unit, date.getTime()));
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		currentSupply = row.GetItemValue(table.GetColumn(4));
		currentSupply = currentSupply.substring(0, currentSupply.indexOf("/")).trim();
		assertEquals("1", currentSupply);
		
		//test supply in the double digits
		for (int i = 0; i < 10; i++) {
			hit.GetItemIndex().AddItem(product.CreateItem(unit, date.getTime()));
		}
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		currentSupply = row.GetItemValue(table.GetColumn(4));
		currentSupply = currentSupply.substring(0, currentSupply.indexOf("/")).trim();
		assertEquals("11", currentSupply);
	}
	
	@Test
	public void testMaxCurrentAge() {
		//setup time stuff
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1);
		Calendar date = (Calendar) startDate.clone();
		date.add(Calendar.DAY_OF_MONTH, 2);
		
		//set up objects
		StorageUnit unit = new StorageUnit("Unit");
		hit.AddStorageUnit(unit);
		Product product = InitializeNewProduct("product", date.getTime());
		
		//test that the oldest item is 0 days old
		Item item1 = product.CreateItem(unit, new Date());
		hit.GetItemIndex().AddItem(item1);
		
		ProductStatisticsReport report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		List<BuilderObject> builderObjects = builder.GetOutput();
		BuilderTable table = (BuilderTable) builderObjects.get(0);
		
		TableRow row = (TableRow) table.GetRow(0);
		String maxAge = row.GetItemValue(table.GetColumn(9));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("0 days", maxAge);
		
		//test that the oldest item is older than its expiration date
		product.SetShelfLife(1);
		startDate.add(Calendar.MONTH, -1);
		startDate.add(Calendar.DAY_OF_MONTH, 2);
		Item expiredItem = product.CreateItem(unit, startDate.getTime());
		hit.GetItemIndex().AddItem(expiredItem);
		
		report = new ProductStatisticsReport(2);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(9));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals(daysBetween(startDate.getTime(), new Date()) + " days", maxAge);
		
		//test that the oldest item is one day old
		hit.GetItemIndex().RemoveItem(expiredItem);
		startDate = Calendar.getInstance();
		startDate.add(Calendar.DAY_OF_MONTH, -1);
		Item oneDayOld = product.CreateItem(unit, startDate.getTime());
		hit.GetItemIndex().AddItem(oneDayOld);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(9));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("1 days", maxAge);
		
		//test that a newer item is chosen over an older removed item
		report = new ProductStatisticsReport(2);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(9));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("1 days", maxAge);
		
		//test that year is considered in choosing make year
		hit.GetItemIndex().RemoveItem(oneDayOld);
		Calendar oneYearAgo = Calendar.getInstance();
		oneYearAgo.add(Calendar.YEAR, -1);
		Calendar twoDaysAgo = Calendar.getInstance();
		twoDaysAgo.add(Calendar.DAY_OF_MONTH, -2);
		Item oneYearAgoItem = product.CreateItem(unit, oneYearAgo.getTime());
		Item twoDaysAgoItem = product.CreateItem(unit, twoDaysAgo.getTime());
		hit.GetItemIndex().AddItem(oneYearAgoItem);
		hit.GetItemIndex().AddItem(twoDaysAgoItem);
		
		//test that a newer item is chosen over an older removed item
		report = new ProductStatisticsReport(2);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(9));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals(daysBetween(oneYearAgo.getTime(), new Date()) + " days", maxAge);
	}
	
	@Test 
	public void testAverageCurrentAge() {
		//setup time stuff
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1);
		
		//set up objects
		StorageUnit unit = new StorageUnit("Unit");
		hit.AddStorageUnit(unit);
		Product product = InitializeNewProduct("product", startDate.getTime());
		
		//test that all items are removed so there is no average current age
		Item item = product.CreateItem(unit, new Date());
		hit.GetItemIndex().AddItem(item);
		hit.GetItemIndex().RemoveItem(item);
		ProductStatisticsReport report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		List<BuilderObject> builderObjects = builder.GetOutput();
		BuilderTable table = (BuilderTable) builderObjects.get(0);
		
		TableRow row = (TableRow) table.GetRow(0);
		String avgAge = row.GetItemValue(table.GetColumn(9));
		avgAge = avgAge.substring(0, avgAge.indexOf("/")).trim();
		assertEquals("0 days", avgAge);
		
		//test having three items that are the same age
		Calendar twoDaysAgo = Calendar.getInstance();
		twoDaysAgo.add(Calendar.DAY_OF_MONTH, -2);
		Item item1 = product.CreateItem(unit, twoDaysAgo.getTime());
		Item item2 = product.CreateItem(unit, twoDaysAgo.getTime());
		Item item3 = product.CreateItem(unit, twoDaysAgo.getTime());
		hit.GetItemIndex().AddItem(item1);
		hit.GetItemIndex().AddItem(item2);
		hit.GetItemIndex().AddItem(item3);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		avgAge = row.GetItemValue(table.GetColumn(9));
		avgAge = avgAge.substring(0, avgAge.indexOf("/")).trim();
		assertEquals("2 days", avgAge);
		
		hit.GetItemIndex().RemoveItem(item1);
		hit.GetItemIndex().RemoveItem(item2);
		hit.GetItemIndex().RemoveItem(item3);
		
		//test the case that the items are different ages
		Calendar fourDaysAgo = Calendar.getInstance();
		fourDaysAgo.add(Calendar.DAY_OF_MONTH, -4);
		Item twoDaysAgoItem = product.CreateItem(unit, twoDaysAgo.getTime());
		Item fourDaysAgoItem = product.CreateItem(unit, fourDaysAgo.getTime());
		hit.GetItemIndex().AddItem(twoDaysAgoItem);
		hit.GetItemIndex().AddItem(fourDaysAgoItem);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		avgAge = row.GetItemValue(table.GetColumn(9));
		avgAge = avgAge.substring(0, avgAge.indexOf("/")).trim();
		assertEquals("3 days", avgAge);
		
		hit.GetItemIndex().RemoveItem(twoDaysAgoItem);
		hit.GetItemIndex().RemoveItem(fourDaysAgoItem);
		
		//test the case that there's only one item
		item = product.CreateItem(unit, fourDaysAgo.getTime());
		hit.GetItemIndex().AddItem(item);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		avgAge = row.GetItemValue(table.GetColumn(9));
		avgAge = avgAge.substring(0, avgAge.indexOf("/")).trim();
		assertEquals("4 days", avgAge);
	}
	
	@Test
	public void testMaxUsedAge() {
		//setup time stuff
		Calendar startDate = Calendar.getInstance();
		startDate.add(Calendar.MONTH, -1);
		
		//set up objects
		StorageUnit unit = new StorageUnit("Unit");
		hit.AddStorageUnit(unit);
		Product product = InitializeNewProduct("product", startDate.getTime());
		
		
		//test the case that no items have been used yet
		Item item = product.CreateItem(unit, new Date());
		hit.GetItemIndex().AddItem(item);
		ProductStatisticsReport report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		List<BuilderObject> builderObjects = builder.GetOutput();
		BuilderTable table = (BuilderTable) builderObjects.get(0);
		
		TableRow row = (TableRow) table.GetRow(0);
		String maxAge = row.GetItemValue(table.GetColumn(8));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("0 days", maxAge);
		
		//test the case where the max used age is 0
		hit.GetItemIndex().RemoveItem(item);
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(8));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("0 days", maxAge);
		
		//test the case that the max used age is less than a month
		Calendar yesterday = Calendar.getInstance();
		yesterday.add(Calendar.DAY_OF_MONTH, -1);
		Item lessThanAMonth = product.CreateItem(unit, yesterday.getTime());
		hit.GetItemIndex().AddItem(lessThanAMonth);
		hit.GetItemIndex().RemoveItem(lessThanAMonth);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(8));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals("1 days", maxAge);
		
		//test the case that the max age is older than a month
		Calendar aMonthAgo = Calendar.getInstance();
		aMonthAgo.add(Calendar.MONTH, -1);
		aMonthAgo.add(Calendar.DAY_OF_MONTH, -20);
		Item aMonthAgoItem = product.CreateItem(unit, aMonthAgo.getTime());
		hit.GetItemIndex().AddItem(aMonthAgoItem);
		hit.GetItemIndex().RemoveItem(aMonthAgoItem);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(8));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals(daysBetween(aMonthAgo.getTime(), new Date()) + " days", maxAge);
		
		//test the case that the item was used at a year old
		Calendar aYearAgo = Calendar.getInstance();
		aYearAgo.add(Calendar.YEAR, -1);
		aYearAgo.add(Calendar.DAY_OF_MONTH, -4);
		Item aYearAgoItem = product.CreateItem(unit, aYearAgo.getTime());
		hit.GetItemIndex().AddItem(aYearAgoItem);
		hit.GetItemIndex().RemoveItem(aYearAgoItem);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		maxAge = row.GetItemValue(table.GetColumn(8));
		maxAge = maxAge.substring(maxAge.indexOf("/") + 1).trim();
		assertEquals(daysBetween(aYearAgo.getTime(), new Date()) + " days", maxAge);
	}
	
	@Test
	public void testAverageUsedAge() {
		//setup time stuff
		Calendar prodDate = Calendar.getInstance();
		prodDate.add(Calendar.MONTH, -4);
		
		//set up objects
		StorageUnit unit = new StorageUnit("Unit");
		hit.AddStorageUnit(unit);
		Product product = InitializeNewProduct("product", prodDate.getTime());
		
		//test the case that there are items removed before the report
		Calendar beforeReport = Calendar.getInstance();
		beforeReport.add(Calendar.MONTH, -1);
		beforeReport.add(Calendar.DAY_OF_MONTH, -4);
		Calendar twoDaysAgo = Calendar.getInstance();
		twoDaysAgo.add(Calendar.DAY_OF_MONTH, -2);
		
		Item beforeReportItem = this.itemFacade.AddItems(product, 1, beforeReport.getTime(), unit).get(0);
		hit.GetItemIndex().RemoveItem(beforeReportItem, beforeReport.getTime());
		
		Item twoDaysAgoItem = product.CreateItem(unit, twoDaysAgo.getTime());
		hit.GetItemIndex().AddItem(twoDaysAgoItem);
		hit.GetItemIndex().RemoveItem(twoDaysAgoItem);
		
		ProductStatisticsReport report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		report.BuildReport(builder);
		
		List<BuilderObject> builderObjects = builder.GetOutput();
		BuilderTable table = (BuilderTable) builderObjects.get(0);
		
		TableRow row = (TableRow) table.GetRow(0);
		String avgAge = row.GetItemValue(table.GetColumn(8));
		avgAge = avgAge.substring(avgAge.indexOf("/") + 1).trim();
		assertEquals("2 days", avgAge);
		
		//test the case that one of the dates was added outside the report period
		Date exitDate = new Date();
		beforeReportItem.SetExit(exitDate);
		
		report = new ProductStatisticsReport(1);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);		
		builder = new TestBuilder();
		report.BuildReport(builder);
		
		builderObjects = builder.GetOutput();
		table = (BuilderTable) builderObjects.get(0);
		
		row = (TableRow) table.GetRow(0);
		avgAge = row.GetItemValue(table.GetColumn(8));
		avgAge = avgAge.substring(0, avgAge.indexOf("/")).trim();
		
		int temp = daysBetween(beforeReport.getTime(), exitDate);
		int trueAvg = (temp + 2) / 2;
		assertEquals(trueAvg + " days", avgAge);
	}
	
	// Preston - Test Case 6
	@Test
	public void testValidReportStartDates()
	{
		Calendar endDate = Calendar.getInstance();
		endDate.clear();
		endDate.set(Calendar.YEAR, 2013);
		endDate.set(Calendar.MONTH, Calendar.MAY);
		endDate.set(Calendar.DAY_OF_MONTH, 31);
		endDate.set(Calendar.HOUR, 12);
		endDate.set(Calendar.MINUTE, 30);
		
		// Test one month before May 31st (April 30th).
		Calendar testDate1 = Calendar.getInstance();
		testDate1.clear();
		testDate1.set(Calendar.YEAR, 2013);
		testDate1.set(Calendar.MONTH, Calendar.APRIL);
		testDate1.set(Calendar.DAY_OF_MONTH, 30);
		testDate1.set(Calendar.HOUR, 12);
		testDate1.set(Calendar.MINUTE, 30);
		
		ProductStatisticsReport report = new ProductStatisticsReport(1, endDate);
		assertEquals(report.getStartDate(), testDate1);
		
		// Test two months before May 31st (March 31st).
		Calendar testDate2 = Calendar.getInstance();
		testDate2.clear();
		testDate2.set(Calendar.YEAR, 2013);
		testDate2.set(Calendar.MONTH, Calendar.MARCH);
		testDate2.set(Calendar.DAY_OF_MONTH, 31);
		testDate2.set(Calendar.HOUR, 12);
		testDate2.set(Calendar.MINUTE, 30);
		
		report = new ProductStatisticsReport(2, endDate);
		assertEquals(report.getStartDate(), testDate2);
		
		// Test three months before May 31st, 2013 - not leap year (February 28th)
		Calendar testDate3 = Calendar.getInstance();
		testDate3.clear();
		testDate3.set(Calendar.YEAR, 2013);
		testDate3.set(Calendar.MONTH, Calendar.FEBRUARY);
		testDate3.set(Calendar.DAY_OF_MONTH, 28);
		testDate3.set(Calendar.HOUR, 12);
		testDate3.set(Calendar.MINUTE, 30);
		
		report = new ProductStatisticsReport(3, endDate);
		assertEquals(report.getStartDate(), testDate3);
		

		// Check to make sure the product added the first day of the reporting period.
		Calendar createDate = Calendar.getInstance();
		createDate.clear();
		createDate.setTimeInMillis(testDate3.getTimeInMillis());
		createDate.add(Calendar.HOUR, 1);
		InitializeNewProduct("Testing123", createDate.getTime());
		
		report = new ProductStatisticsReport(3, endDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		BuilderTable table = (BuilderTable)builder.GetOutput().get(0);
		assertEquals(table.GetRowCount(), 1);
		TableRow row = table.GetRow(0);
		TableColumn nameColumn = table.GetColumn(0);
		String name = row.GetItemValue(nameColumn);
		assertEquals(name, "Testing123");
		
		// Check to make sure that products added just before start date show up.
		Calendar createDate2 = Calendar.getInstance();
		createDate2.clear();
		createDate2.setTimeInMillis(testDate3.getTimeInMillis());
		createDate2.add(Calendar.DAY_OF_MONTH, -2);
		InitializeNewProduct("Testing321", createDate2.getTime());
		
		report = new ProductStatisticsReport(3, endDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		assertEquals(table.GetRowCount(), 2);
		row = table.GetRow(1);
		nameColumn = table.GetColumn(0);
		name = row.GetItemValue(nameColumn);
		assertEquals(name, "Testing321");
	}
	
	// Preston - Test Case 5
	@Test
	public void testProductLeapYear()
	{
		// Initilize the three products
		Calendar testDate1 = Calendar.getInstance();
		testDate1.clear();
		testDate1.set(Calendar.YEAR, 2012);
		testDate1.set(Calendar.MONTH, Calendar.FEBRUARY);
		testDate1.set(Calendar.DAY_OF_MONTH, 28);
		testDate1.set(Calendar.HOUR, 23);
		testDate1.set(Calendar.MINUTE, 45);
		
		this.InitializeNewProduct("BeforeLeapYear", testDate1.getTime());
		
		Calendar testDate2 = Calendar.getInstance();
		testDate2.clear();
		testDate2.set(Calendar.YEAR, 2012);
		testDate2.set(Calendar.MONTH, Calendar.FEBRUARY);
		testDate2.set(Calendar.DAY_OF_MONTH, 29);
		testDate2.set(Calendar.HOUR, 7);
		testDate2.set(Calendar.MINUTE, 30);
		
		this.InitializeNewProduct("OnLeapYear", testDate2.getTime());
		
		Calendar testDate3 = Calendar.getInstance();
		testDate3.clear();
		testDate3.set(Calendar.YEAR, 2012);
		testDate3.set(Calendar.MONTH, Calendar.MARCH);
		testDate3.set(Calendar.DAY_OF_MONTH, 1);
		testDate3.set(Calendar.HOUR, 1);
		testDate3.set(Calendar.MINUTE, 15);
		
		this.InitializeNewProduct("AfterLeapYear", testDate3.getTime());
		
		// Check report that ends before first product added.
		Calendar endDate = Calendar.getInstance();
		endDate.clear();
		endDate.set(Calendar.YEAR, 2012);
		endDate.set(Calendar.MONTH, Calendar.FEBRUARY);
		endDate.set(Calendar.DAY_OF_MONTH, 27);
		endDate.set(Calendar.HOUR, 8);
		endDate.set(Calendar.MINUTE, 20);
		
		ProductStatisticsReport report = new ProductStatisticsReport(3, endDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		BuilderTable table = (BuilderTable)builder.GetOutput().get(0);
		assertEquals(table.GetRowCount(), 0);
		
		// Check report that ends on Leap Year.
		endDate.clear();
		endDate.set(Calendar.YEAR, 2012);
		endDate.set(Calendar.MONTH, Calendar.FEBRUARY);
		endDate.set(Calendar.DAY_OF_MONTH, 29);
		endDate.set(Calendar.HOUR, 18);
		endDate.set(Calendar.MINUTE, 30);
		
		report = new ProductStatisticsReport(3, endDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		assertEquals(table.GetRowCount(), 2);
		
		// Check report that ends after Leap Year
		endDate.clear();
		endDate.set(Calendar.YEAR, 2012);
		endDate.set(Calendar.MONTH, Calendar.MARCH);
		endDate.set(Calendar.DAY_OF_MONTH, 1);
		endDate.set(Calendar.HOUR, 12);
		endDate.set(Calendar.MINUTE, 00);
		
		report = new ProductStatisticsReport(3, endDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		assertEquals(table.GetRowCount(), 3);
	}
	
	// Preston / Case 7
	@Test
	public void testAddedItemBoundries()
	{
		Calendar testDate1 = Calendar.getInstance();
		testDate1.clear();
		testDate1.set(Calendar.YEAR, 2010);
		testDate1.set(Calendar.MONTH, Calendar.JANUARY);
		testDate1.set(Calendar.DAY_OF_MONTH, 1);
		testDate1.set(Calendar.HOUR, 2);
		testDate1.set(Calendar.MINUTE, 0);
		Product testProduct = this.InitializeNewProduct("TestProduct", testDate1.getTime());
		
		Calendar reportEndDate = Calendar.getInstance();
		reportEndDate.clear();
		reportEndDate.set(Calendar.YEAR, 2013);
		reportEndDate.set(Calendar.MONTH, Calendar.MARCH);
		reportEndDate.set(Calendar.DAY_OF_MONTH, 15);
		reportEndDate.set(Calendar.HOUR, 0);
		reportEndDate.set(Calendar.MINUTE, 0);
		
		// Test item that is added immediately after report end date. Should not show up.
		Calendar entryDate1 = Calendar.getInstance();
		entryDate1.clear();
		entryDate1.set(Calendar.YEAR, 2013);
		entryDate1.set(Calendar.MONTH, Calendar.MARCH);
		entryDate1.set(Calendar.DAY_OF_MONTH, 16);
		entryDate1.set(Calendar.HOUR, 2);
		entryDate1.set(Calendar.MINUTE, 0);
		
		this.itemFacade.AddItems(testProduct, 1, entryDate1.getTime(), container);
		
		ProductStatisticsReport report = new ProductStatisticsReport(1, reportEndDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		BuilderTable table = (BuilderTable)builder.GetOutput().get(0);
		TableRow row = table.GetRow(0);
		TableColumn supMinMaxCol = table.GetColumn(5);
		TableColumn supUsedAddedCol = table.GetColumn(6);
		String supMinMax = row.GetItemValue(supMinMaxCol);
		String supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "0 / 0");
		assertEquals(supUsedAdded, "0 / 0");
		
		// Test item that is added immediately before report end date.
		Calendar entryDate2 = (Calendar)reportEndDate.clone();
		entryDate2.add(Calendar.MINUTE, -10);
		
		this.itemFacade.AddItems(testProduct, 1, entryDate2.getTime(), container);
		
		report = new ProductStatisticsReport(1, reportEndDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		row = table.GetRow(0);
		supMinMaxCol = table.GetColumn(5);
		supUsedAddedCol = table.GetColumn(6);
		supMinMax = row.GetItemValue(supMinMaxCol);
		supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "0 / 1");
		assertEquals(supUsedAdded, "0 / 1");
		
		// Test item that is added immediately after the report startDate.
		Calendar entryDate3 = Calendar.getInstance();
		entryDate3.clear();
		entryDate3.set(Calendar.YEAR, 2013);
		entryDate3.set(Calendar.MONTH, Calendar.FEBRUARY);
		entryDate3.set(Calendar.DAY_OF_MONTH, 16);
		entryDate3.set(Calendar.HOUR, 2);
		entryDate3.set(Calendar.MINUTE, 0);
		
		this.itemFacade.AddItems(testProduct, 1, entryDate3.getTime(), container);
		
		report = new ProductStatisticsReport(1, reportEndDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		row = table.GetRow(0);
		supMinMaxCol = table.GetColumn(5);
		supUsedAddedCol = table.GetColumn(6);
		supMinMax = row.GetItemValue(supMinMaxCol);
		supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "0 / 2");
		assertEquals(supUsedAdded, "0 / 2");
		
		// Test item that is added immediately after the report startDate.
		Calendar entryDate4 = Calendar.getInstance();
		entryDate4.clear();
		entryDate4.set(Calendar.YEAR, 2013);
		entryDate4.set(Calendar.MONTH, Calendar.FEBRUARY);
		entryDate4.set(Calendar.DAY_OF_MONTH, 14);
		entryDate4.set(Calendar.HOUR, 23);
		entryDate4.set(Calendar.MINUTE, 59);
		
		this.itemFacade.AddItems(testProduct, 1, entryDate4.getTime(), container).get(0);
		
		report = new ProductStatisticsReport(1, reportEndDate);
		hit.GetProductIndex().Accept(report);
		hit.GetItemIndex().Accept(report);
		builder.Clear();
		report.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		row = table.GetRow(0);
		supMinMaxCol = table.GetColumn(5);
		supUsedAddedCol = table.GetColumn(6);
		supMinMax = row.GetItemValue(supMinMaxCol);
		supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "1 / 3");
		assertEquals(supUsedAdded, "0 / 2");
	}
	
	// Preston - Case 4
	public void testExistingProductIgnored()
	{
		Calendar reportEndDate = Calendar.getInstance();
		reportEndDate.clear();
		reportEndDate.set(Calendar.YEAR, 2013);
		reportEndDate.set(Calendar.MONTH, Calendar.MARCH);
		reportEndDate.set(Calendar.DAY_OF_MONTH, 15);
		reportEndDate.set(Calendar.HOUR, 0);
		reportEndDate.set(Calendar.MINUTE, 0);
		
		int reportLength = 1;
		
		// Test product added right before report start date.
		Calendar testProdDate1 = (Calendar)reportEndDate.clone();
		testProdDate1.add(Calendar.MONTH, -1 * reportLength);
		testProdDate1.add(Calendar.DAY_OF_MONTH, -1);
		
		Product testProduct1 = this.InitializeNewProduct("testProduct1", testProdDate1.getTime());
		
		Calendar testItemDate1 = (Calendar)testProdDate1.clone();
		testItemDate1.add(Calendar.HOUR, 1);
		
		Calendar testRemDate1 = (Calendar)testItemDate1.clone();
		testRemDate1.add(Calendar.HOUR, 1);
		
		List<Item> items = this.itemFacade.AddItems(testProduct1, 5, testItemDate1.getTime(), container);
		this.itemFacade.RemoveItem(items.get(1), testRemDate1.getTime());
		this.itemFacade.RemoveItem(items.get(3), testRemDate1.getTime());
		
		ProductStatisticsReport report1 = new ProductStatisticsReport(reportLength, reportEndDate);
		builder.Clear();
		hit.GetProductIndex().Accept(report1);
		hit.GetItemIndex().Accept(report1);
		report1.BuildReport(builder);
		
		BuilderTable table = (BuilderTable)builder.GetOutput().get(0);
		TableRow row = table.GetRow(0);
		TableColumn supMinMaxCol = table.GetColumn(5);
		TableColumn supUsedAddedCol = table.GetColumn(6);
		String supMinMax = row.GetItemValue(supMinMaxCol);
		String supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "3 / 3"); // Only the end is noticed.
		assertEquals(supUsedAdded, "0 / 0"); // Adds and removes not seen.
		
		// Test product added a long time ago.
		Calendar testProdDate2 = (Calendar)reportEndDate.clone();
		testProdDate2.add(Calendar.MONTH, -12);
		
		Product testProduct2 = this.InitializeNewProduct("testProduct2", testProdDate2.getTime());
		
		Calendar testItemDate2 = (Calendar)testProdDate2.clone();
		testItemDate2.add(Calendar.HOUR, 1);
		
		Calendar testRemDate2 = (Calendar)testItemDate2.clone();
		testRemDate2.add(Calendar.HOUR, 1);
		
		List<Item> items2 = this.itemFacade.AddItems(testProduct2, 7, testItemDate2.getTime(), container);
		this.itemFacade.RemoveItem(items2.get(1), testRemDate2.getTime());
		this.itemFacade.RemoveItem(items2.get(3), testRemDate2.getTime());
		this.itemFacade.RemoveItem(items2.get(4), testRemDate2.getTime());
		
		ProductStatisticsReport report2 = new ProductStatisticsReport(reportLength, reportEndDate);
		builder.Clear();
		hit.GetProductIndex().Accept(report2);
		hit.GetItemIndex().Accept(report2);
		report2.BuildReport(builder);
		
		table = (BuilderTable)builder.GetOutput().get(0);
		row = table.GetRow(1);
		supMinMaxCol = table.GetColumn(5);
		supUsedAddedCol = table.GetColumn(6);
		supMinMax = row.GetItemValue(supMinMaxCol);
		supUsedAdded = row.GetItemValue(supUsedAddedCol);
		assertEquals(supMinMax, "4 / 4"); // Only the end is noticed.
		assertEquals(supUsedAdded, "0 / 0"); // Adds and removes not seen.
	}
	
	
	 public int daysBetween(Date d1, Date d2){
         return (int) ((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
	 }
	
	private Product InitializeNewProduct(String name, Date creation){
		Product p = new Product();
		p.SetDescription(name);
		p.SetCreation(creation);
		p.SetBarcode(new Barcode(name.toLowerCase()));
		p.AddProductContainer(container);
		p.SetSize(new Size(1, Size.Unit.COUNT));
		p.SetShelfLife(0);
		p.SetThreeMonth(0);
		hit.GetProductFacade().AddProduct(p);
		return p;
	}
}